﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Net.Mime;
using System.Drawing.Imaging;

namespace Pikapika.Areas.Admin.Controllers
{
    [Authorize(Roles = "Administrators,Editors")]
    public class FileController : Controller
    {
        private const int SIZE_LIMIT = 2 * 1024 * 1024;

        private static readonly Dictionary<Guid, string> _knownFormatExtensions = new Dictionary<Guid, string>();

        static FileController()
        {
            _knownFormatExtensions.Add(ImageFormat.Bmp.Guid, "bmp");
            _knownFormatExtensions.Add(ImageFormat.Gif.Guid, "gif");
            _knownFormatExtensions.Add(ImageFormat.Jpeg.Guid, "jpg");
            _knownFormatExtensions.Add(ImageFormat.Png.Guid, "png");
            _knownFormatExtensions.Add(ImageFormat.Tiff.Guid, "tif");
        }

        [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
        private ActionResult ProcessUploadedImage(Stream inputStream, string fileName)
        {
            var storeFileName = string.Format("{0:yyyy}\\{0:yyyyMMdd}\\{0:yyyyMMdd-HHmmss}-{1:x8}", DateTime.UtcNow, BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0));
            if (!string.IsNullOrWhiteSpace(fileName))
            {
                storeFileName = Path.ChangeExtension(storeFileName, Path.GetExtension(fileName));
            }
            var storeFullPath = Path.Combine(Server.MapPath("~/Upload/Images"), storeFileName);
            var storeDirectory = Path.GetDirectoryName(storeFullPath);
            try
            {
                if (!Directory.Exists(storeDirectory))
                {
                    Directory.CreateDirectory(storeDirectory);
                }
                using (var fs = System.IO.File.Create(storeFullPath))
                {
                    inputStream.CopyTo(fs);
                }
            }
            catch (Exception ex)
            {
                return Content(string.Format("{{\"error\":\"Can't save file: {0}\"}}", Server.HtmlEncode(ex.Message)));
            }
            if (new FileInfo(storeFullPath).Length > SIZE_LIMIT)
            {
                System.IO.File.Delete(storeFullPath);
                return Content("{\"error\":\"File is too large\"}");
            }
            int width, height;
            string extension;
            try
            {
                using (var img = System.Drawing.Image.FromFile(storeFullPath))
                {
                    width = img.Width;
                    height = img.Height;
                    if (!_knownFormatExtensions.TryGetValue(img.RawFormat.Guid, out extension)) {
                        return Content("{\"error\":\"Unsupported format\"}");
                    }
                }
                if (!string.Equals(Path.GetExtension(storeFullPath), "." + extension))
                {
                    var newPath = Path.ChangeExtension(storeFullPath, extension);
                    System.IO.File.Move(storeFullPath, newPath);
                    storeFullPath = newPath;
                    storeFileName = Path.ChangeExtension(storeFileName, extension);
                }
            }
            catch
            {
                System.IO.File.Delete(storeFullPath);
                return Content("{\"error\":\"File is not an image\"}");
            }
            return Content(string.Format("{{\"success\":true, \"fileName\":\"/Upload/Images/{0}\", \"width\": {1}, \"height\": {2}}}", storeFileName.Replace("\\", "/"), width, height));
        }

        private HttpStatusCodeResult HttpBadRequest()
        {
            return new HttpStatusCodeResult(400, "Bad request");
        }

        [HttpPost]
        public ActionResult UploadImage()
        {
            if (Request.IsAjaxRequest())
            {
                var fileName = Request.Headers["X-File-Name"];
                if (string.IsNullOrWhiteSpace(fileName))
                {
                    return HttpBadRequest();
                }
                return ProcessUploadedImage(Request.InputStream, fileName);
            }
            else
            {
                if (Request.Files.Count == 0)
                {
                    return HttpBadRequest();
                }
                return ProcessUploadedImage(Request.Files[0].InputStream, Request.Files[0].FileName);
            }
        }

        public ActionResult GetWebImage(Uri url, string cookies = null)
        {
            if (url == null)
            {
                return HttpBadRequest();
            }
            // prevent users from stealing internal files
            if (url.IsFile || url.IsLoopback || url.IsUnc || !url.IsAbsoluteUri)
            {
                return HttpBadRequest();
            }
            if (string.Equals(url.DnsSafeHost, Request.Url.DnsSafeHost, StringComparison.OrdinalIgnoreCase))
            {
                return Content("{\"error\":\"Can't download image in this website\"}");
            }
            try
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
                if (!string.IsNullOrWhiteSpace(cookies))
                {
                    req.UserAgent = Request.UserAgent; // some website uses user-agent for authenication too
                    req.CookieContainer = new CookieContainer();
                    req.CookieContainer.SetCookies(url, cookies.Replace(";", ","));
                }
                using (var resp = (HttpWebResponse)req.GetResponse())
                {
                    using (var s = resp.GetResponseStream())
                    {
                        string fileName = url.LocalPath;
                        var contentDisposition = resp.Headers["Content-Disposition"];
                        if (!string.IsNullOrWhiteSpace(contentDisposition))
                        {
                            try
                            {
                                var cdObj = new ContentDisposition(contentDisposition);
                                if (!string.IsNullOrWhiteSpace(cdObj.FileName))
                                {
                                    fileName = cdObj.FileName;
                                }
                            }
                            catch
                            {
                            }
                        }
                        return ProcessUploadedImage(s, fileName);
                    }
                }
            }
            catch (Exception ex)
            {
                return Content(string.Format("{{\"error\":\"Can't download the image: {0}\"}}", Server.HtmlEncode(ex.Message)));
            }
        }
    }
}
